package ru.ifmo.thesis.solver;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.ifmo.thesis.domain.AbstractGeoService;
import ru.ifmo.thesis.domain.GeoServiceFactory;
import ru.ifmo.thesis.domain.Job;
import ru.ifmo.thesis.domain.Location;
import ru.ifmo.thesis.domain.Shedule;
import ru.ifmo.thesis.domain.Worker;
import ru.ifmo.thesis.quantities.Minutes;
import ru.ifmo.thesis.task.Task;
import ru.ifmo.thesis.util.Algorithms;

public class DirectSolver implements ISolver {

	private static final Logger logger = LoggerFactory.getLogger(DirectSolver.class);
	
	private static final int ITERATIONS_LOG = 10000;
	
	private static AbstractGeoService geoService = GeoServiceFactory.getGeoService();
	
	private double bestCost = Double.MAX_VALUE;

	private int iteration = 0;
	
	@Override
	public Shedule solve(Task t) {
		return findSolution(t.getWorkers(), t.getJobs());
	}
	
	private Shedule findSolution(List<Worker> workers,
			List<Job> taskList) {
		
		Location[] startPoint = new Location[workers.size()];
		for (int i = 0; i < startPoint.length; i++) {
			startPoint[i] = geoService.getOfficeLocation();
		}
		
		return findSolutionInternal(new Shedule(), workers, taskList, 
				startPoint);
	}
	
	private Shedule findSolutionInternal(
			Shedule shedule, 
			List<Worker> workers,
			List<Job> taskList,
			Location[] prevLocations) {
		
		logIterations();
		if (taskList.isEmpty()) {
			double cost = Algorithms.calculateSheduleCost(shedule);
			if (cost < bestCost) {
				bestCost = cost;
			}
			return shedule;
		}
		
		Shedule bestSolution = null;
		for (Worker w : workers) {
		
			Shedule newShedule = copyShedule(shedule);
			
			List<Job> newTaskList = new ArrayList<Job>(taskList);
			for (int i = 0; i < newTaskList.size(); i++) {
				//Get next task
				Job t = newTaskList.remove(i);
				//assign task to selected worker
				newShedule.appendJob(w, t);
				
				Location[] newPrevLocations = new Location[prevLocations.length];
				System.arraycopy(prevLocations, 0, newPrevLocations, 0, prevLocations.length);
			    newPrevLocations[w.getId()] = t.getLocation();
				
				Shedule newSolution = 
					findSolutionInternal(newShedule, workers, newTaskList, 
							newPrevLocations);
				
				if (newSolution == null) {
					continue;
				}
				
				if (bestSolution == null) {
					bestSolution = newSolution;
				} else {
					if (Algorithms.calculateSheduleCost(newSolution) < Algorithms.calculateSheduleCost(bestSolution)) {
						bestSolution = newSolution;
					}
				}
			}
		}
		
		return bestSolution;
	}

	private Shedule copyShedule(Shedule shedule) {
		try {
			return (Shedule) shedule.clone();
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e);
		}
	}
	
	private void logIterations() {
		iteration  += 1;
		if (iteration % ITERATIONS_LOG == 0) {
			logger.debug(iteration + " iterations done");
		}
	}
}
